Srange¶
The command srange
gives a more general version of range
that works with Sage number types. Here are some examples of its use:
srange(5, 10, 1/3)
[5, 16/3, 17/3, 6, 19/3, 20/3, 7, 22/3, 23/3, 8, 25/3, 26/3, 9, 28/3, 29/3]
f(x) = sin(x)
lst = []
for x in srange(0, 2*pi, 1/10):
lst.append( (x, f(x)) )
lst
[(0, 0), (1/10, sin(1/10)), (1/5, sin(1/5)), (3/10, sin(3/10)), (2/5, sin(2/5)), (1/2, sin(1/2)), (3/5, sin(3/5)), (7/10, sin(7/10)), (4/5, sin(4/5)), (9/10, sin(9/10)), (1, sin(1)), (11/10, sin(11/10)), (6/5, sin(6/5)), (13/10, sin(13/10)), (7/5, sin(7/5)), (3/2, sin(3/2)), (8/5, sin(8/5)), (17/10, sin(17/10)), (9/5, sin(9/5)), (19/10, sin(19/10)), (2, sin(2)), (21/10, sin(21/10)), (11/5, sin(11/5)), (23/10, sin(23/10)), (12/5, sin(12/5)), (5/2, sin(5/2)), (13/5, sin(13/5)), (27/10, sin(27/10)), (14/5, sin(14/5)), (29/10, sin(29/10)), (3, sin(3)), (31/10, sin(31/10)), (16/5, sin(16/5)), (33/10, sin(33/10)), (17/5, sin(17/5)), (7/2, sin(7/2)), (18/5, sin(18/5)), (37/10, sin(37/10)), (19/5, sin(19/5)), (39/10, sin(39/10)), (4, sin(4)), (41/10, sin(41/10)), (21/5, sin(21/5)), (43/10, sin(43/10)), (22/5, sin(22/5)), (9/2, sin(9/2)), (23/5, sin(23/5)), (47/10, sin(47/10)), (24/5, sin(24/5)), (49/10, sin(49/10)), (5, sin(5)), (51/10, sin(51/10)), (26/5, sin(26/5)), (53/10, sin(53/10)), (27/5, sin(27/5)), (11/2, sin(11/2)), (28/5, sin(28/5)), (57/10, sin(57/10)), (29/5, sin(29/5)), (59/10, sin(59/10)), (6, sin(6)), (61/10, sin(61/10)), (31/5, sin(31/5))]
line2d(lst)
It also works with floating point types:
srange(5.0, 10.0, 0.3)
[5.00000000000000, 5.30000000000000, 5.60000000000000, 5.90000000000000, 6.20000000000000, 6.50000000000000, 6.80000000000000, 7.10000000000000, 7.40000000000000, 7.70000000000000, 8.00000000000000, 8.30000000000000, 8.60000000000000, 8.90000000000000, 9.20000000000000, 9.50000000000000, 9.80000000000000]
lst = []
for x in srange(0, 2*pi, 0.1):
lst.append( (x, f(x)) )
lst
[(0, 0), (0.100000000000000, 0.0998334166468282), (0.200000000000000, 0.198669330795061), (0.300000000000000, 0.295520206661340), (0.400000000000000, 0.389418342308651), (0.500000000000000, 0.479425538604203), (0.600000000000000, 0.564642473395035), (0.700000000000000, 0.644217687237691), (0.800000000000000, 0.717356090899523), (0.900000000000000, 0.783326909627483), (1.00000000000000, 0.841470984807896), (1.10000000000000, 0.891207360061435), (1.20000000000000, 0.932039085967226), (1.30000000000000, 0.963558185417193), (1.40000000000000, 0.985449729988460), (1.50000000000000, 0.997494986604054), (1.60000000000000, 0.999573603041505), (1.70000000000000, 0.991664810452469), (1.80000000000000, 0.973847630878195), (1.90000000000000, 0.946300087687414), (2.00000000000000, 0.909297426825681), (2.10000000000000, 0.863209366648873), (2.20000000000000, 0.808496403819590), (2.30000000000000, 0.745705212176720), (2.40000000000000, 0.675463180551150), (2.50000000000000, 0.598472144103956), (2.60000000000000, 0.515501371821463), (2.70000000000000, 0.427379880233829), (2.80000000000000, 0.334988150155904), (2.90000000000000, 0.239249329213981), (3.00000000000000, 0.141120008059866), (3.10000000000000, 0.0415806624332892), (3.20000000000000, -0.0583741434275814), (3.30000000000000, -0.157745694143250), (3.40000000000000, -0.255541102026833), (3.50000000000000, -0.350783227689622), (3.60000000000000, -0.442520443294854), (3.70000000000000, -0.529836140908495), (3.80000000000000, -0.611857890942721), (3.90000000000000, -0.687766159183975), (4.00000000000000, -0.756802495307929), (4.10000000000000, -0.818277111064411), (4.20000000000000, -0.871575772413589), (4.30000000000000, -0.916165936749455), (4.40000000000000, -0.951602073889516), (4.50000000000000, -0.977530117665097), (4.60000000000000, -0.993691003633464), (4.70000000000000, -0.999923257564101), (4.80000000000000, -0.996164608835841), (4.90000000000000, -0.982452612624333), (5.00000000000000, -0.958924274663139), (5.10000000000000, -0.925814682327733), (5.20000000000000, -0.883454655720154), (5.30000000000000, -0.832267442223903), (5.40000000000000, -0.772764487555989), (5.50000000000000, -0.705540325570394), (5.60000000000000, -0.631266637872324), (5.70000000000000, -0.550685542597641), (5.80000000000000, -0.464602179413761), (5.90000000000000, -0.373876664830241), (5.99999999999999, -0.279415498198931), (6.09999999999999, -0.182162504272101), (6.19999999999999, -0.0830894028175026)]
Rings and Fields¶
Recall that a field is a collection of numbers together with addition and multiplication operations, including an additive and multiplicative identity. There are lots of fields.
A similar object that is missing multiplicative inverses is called a ring
.
Sage has implementations of lots of fields, which may be of interest.
The rationals and the integers¶
The field of rational numbers is denoted $\mathbb Q$ in mathematics and is QQ
in Sage:
QQ
Rational Field
Because it is a field, it has an additive identity (zero) and a multiplicative identity (one):
QQ.one()
1
QQ.zero()
0
The field QQ
is the parent
of a rational number in Sage. For example:
(3/2).parent()
Rational Field
(3/2).parent() == QQ
True
Elements in a field also have an additive inverse. This is given by negation. The multiplicative inverse of an element x
of a field can be written as ~x
.
~(3/2)
2/3
~pi
1/pi
(3/2).parent() == SR
False
SR(3/2).parent()
Symbolic Ring
On the other hand, if you have an integer its parent is different:
4.parent()
Integer Ring
The integers $\mathbb Z$ is a ring, and is ZZ
in Sage:
ZZ
Integer Ring
4.parent() == QQ
False
four = QQ(4)
four.parent()
Rational Field
Sage does smart changes of parents
when doing algebraic manipulations. So even though both 3
and 2
have the integer ring as their parent, the ratio 3/2
has the rational numbers as its parent.
Symbolic ring is the biggest field, so it is where things end up which are not naturally in a smaller field:
(pi + four).parent()
Symbolic Ring
Integer quotients¶
For $n>1$, the quotient space $\mathbb Z/ n \mathbb Z$ is a ring. In the space $\mathbb Z/ n \mathbb Z$ consists of equivalence classes of integers, where two integers are equivalent if they differ by an integer multiple of $n$.
Z_mod_6 = ZZ.quotient_ring(6)
Z_mod_6
Ring of integers modulo 6
Numbers of $\mathbb Z/6 \mathbb Z$ have canonical representatives in the set $\{0, 1, \ldots, 5 = 6-1\}$. We can construct the equivalence class of $9$ in $\mathbb Z/6 \mathbb Z$ as follows: (In general if F
is a field, F(x)
attempts to convert x
to a representative in the field.
Z_mod_6(19)
1
Note that three
looks like the number 3
but its parent is different:
three = Z_mod_6(3)
three
3
three.parent()
Ring of integers modulo 6
Therefore, we have:
three*three
3
Note that $\mathbb Z / 6 \mathbb Z$ is not a field, because there is no $k \in \mathbb Z / 6 \mathbb Z$ such that $2k=1$.
two = Z_mod_6(2)
two
2
~two
--------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) Cell In[52], line 1 ----> 1 ~two File ~/Git/sagemath/sage/src/sage/rings/finite_rings/integer_mod.pyx:2847, in sage.rings.finite_rings.integer_mod.IntegerMod_int.__invert__() 2845 x = self._modulus.inverses[self.ivalue] 2846 if x is None: -> 2847 raise ZeroDivisionError(f"inverse of Mod({self}, {self._modulus.sageInteger}) does not exist") 2848 else: 2849 return x ZeroDivisionError: inverse of Mod(2, 6) does not exist
On the other hand, some elements have inverses:
five = Z_mod_6(11)
five
5
~five
5
Here we construct $\mathbb Z / 7 \mathbb Z$:
Z_mod_7 = ZZ.quotient_ring(7)
Z_mod_7
Ring of integers modulo 7
Here we check that we can multiplicatively invert all elements (besides zero):
for n in Z_mod_7:
if n != 0:
print(f'The inverse of {n} is {~n}')
The inverse of 1 is 1 The inverse of 2 is 4 The inverse of 3 is 5 The inverse of 4 is 2 The inverse of 5 is 3 The inverse of 6 is 6
Thus $\mathbb Z/7 \mathbb Z$ is a field. Sage knows this:
Z_mod_7.is_field()
True
In fact: If $p>1$ is an integer, then the ring $\mathbb Z/p\mathbb Z$ is a field if and only if $p$ is a prime. So,
Z_mod_6.is_field()
False
Presumably, these methods end up calling the is_prime()
method on the integer $p$:
7.is_prime()
True
7.is_prime?
Signature: is_prime(n) -> bool Docstring: Determine whether n is a prime element of its parent ring. INPUT: * "n" -- the object for which to determine primality Exceptional special cases: * For integers, determine whether n is a *positive* prime. * For number fields except \QQ, determine whether n is a prime element *of the maximal order*. ALGORITHM: For integers, this function uses a provable primality test or a strong pseudo-primality test depending on the global "arithmetic proof flag". See also: * "is_pseudoprime()" * "sage.rings.integer.Integer.is_prime()" EXAMPLES: sage: is_prime(389) True sage: is_prime(2000) False sage: is_prime(2) True sage: is_prime(-1) False sage: is_prime(1) False sage: is_prime(-2) False sage: a = 2**2048 + 981 sage: is_prime(a) # not tested - takes ~ 1min sage: proof.arithmetic(False) sage: is_prime(a) # instantaneous! True sage: proof.arithmetic(True) Init docstring: Initialize self. See help(type(self)) for accurate signature. File: ~/Git/sagemath/sage/src/sage/arith/misc.py Type: function
7.is_prime??
Signature: is_prime(n) -> bool Docstring: Determine whether n is a prime element of its parent ring. INPUT: * "n" -- the object for which to determine primality Exceptional special cases: * For integers, determine whether n is a *positive* prime. * For number fields except \QQ, determine whether n is a prime element *of the maximal order*. ALGORITHM: For integers, this function uses a provable primality test or a strong pseudo-primality test depending on the global "arithmetic proof flag". See also: * "is_pseudoprime()" * "sage.rings.integer.Integer.is_prime()" EXAMPLES: sage: is_prime(389) True sage: is_prime(2000) False sage: is_prime(2) True sage: is_prime(-1) False sage: is_prime(1) False sage: is_prime(-2) False sage: a = 2**2048 + 981 sage: is_prime(a) # not tested - takes ~ 1min sage: proof.arithmetic(False) sage: is_prime(a) # instantaneous! True sage: proof.arithmetic(True) Source: def is_prime(n) -> bool: r""" Determine whether `n` is a prime element of its parent ring. INPUT: - ``n`` -- the object for which to determine primality Exceptional special cases: - For integers, determine whether `n` is a *positive* prime. - For number fields except `\QQ`, determine whether `n` is a prime element *of the maximal order*. ALGORITHM: For integers, this function uses a provable primality test or a strong pseudo-primality test depending on the global :mod:`arithmetic proof flag <sage.structure.proof.proof>`. .. SEEALSO:: - :meth:`is_pseudoprime` - :meth:`sage.rings.integer.Integer.is_prime` EXAMPLES:: sage: is_prime(389) True sage: is_prime(2000) False sage: is_prime(2) True sage: is_prime(-1) False sage: is_prime(1) False sage: is_prime(-2) False :: sage: a = 2**2048 + 981 sage: is_prime(a) # not tested - takes ~ 1min sage: proof.arithmetic(False) sage: is_prime(a) # instantaneous! # needs sage.libs.pari True sage: proof.arithmetic(True) TESTS: Make sure the warning from :issue:`25046` works as intended:: sage: is_prime(7/1) doctest:warning ... UserWarning: Testing primality in Rational Field, which is a field, hence the result will always be False. To test whether n is a prime integer, use is_prime(ZZ(n)) or ZZ(n).is_prime(). Using n.is_prime() instead will silence this warning. False sage: ZZ(7/1).is_prime() True sage: QQ(7/1).is_prime() False However, number fields redefine ``.is_prime()`` in an incompatible fashion (cf. :issue:`32340`) and we should not warn:: sage: x = polygen(ZZ, 'x') sage: K.<i> = NumberField(x^2 + 1) # needs sage.rings.number_field sage: is_prime(1 + i) # needs sage.rings.number_field True """ try: ret = n.is_prime() except (AttributeError, NotImplementedError): return ZZ(n).is_prime() R = n.parent() if R.is_field(): # number fields redefine .is_prime(), see #32340 from sage.rings.number_field.number_field_base import NumberField if R is QQ or not isinstance(R, NumberField): import warnings s = f'Testing primality in {R}, which is a field, ' \ 'hence the result will always be False. ' if R is QQ: s += 'To test whether n is a prime integer, use ' \ 'is_prime(ZZ(n)) or ZZ(n).is_prime(). ' s += 'Using n.is_prime() instead will silence this warning.' warnings.warn(s) return ret File: ~/Git/sagemath/sage/src/sage/arith/misc.py Type: function
Symbolic ring¶
The symbolic ring SR
is the parent for all symbolic expressions in Sage.
SR
Symbolic Ring
SR.is_finite()
False
SR.is_field()
True
SR.is_field?
Docstring: Returns True, since the symbolic expression ring is (for the most part) a field. EXAMPLES: sage: SR.is_field() True Init docstring: Initialize self. See help(type(self)) for accurate signature. File: ~/Git/sagemath/sage/src/sage/symbolic/ring.pyx Type: builtin_function_or_method
var('x')
x
x
x
x.parent()
Symbolic Ring
Polynomial rings¶
Given a field $F$ and a variable $x$, the associated ring of polynomials is $F[x]$ and consists of those polynomials of the form $$a_d x^d + a_{d-1} x^{d-1}+ \ldots + a_0,$$ where $a_d, \ldots, a_0 \in F$.
The following defines the ring $Q[x]$. The resulting field is stored in the variable R
. The variable x
si also created, which is the generating indeterminant.
R.<x> = QQ[]
x
x
R
Univariate Polynomial Ring in x over Rational Field
x.parent()
Univariate Polynomial Ring in x over Rational Field
The above notation defines x
to be the generator for the polynomial ring. We can see that x
has this form by calling x.parent()
. (Note that if we construct a variable using var
, we get an element of Symbolic Ring
.)
We can then use polynomial notation to define a polynomial:
p = 3*x^2 - 47
p
3*x^2 - 47
p.parent()==R
True
We can evaluate polynomials
p(3)
-20
The ring $Q[x]$ is not a field because non-constant polynomials do not have multiplicative inverses. But, they do within a larger space:
~p
1/3/(x^2 - 47/3)
show(~p)
This inverse lives in the Fraction Field of Univariate Polynomial Ring in x over Rational Field
:
(~p).parent()
Fraction Field of Univariate Polynomial Ring in x over Rational Field
An integral domain is a ring where the product of any two non-zero elements is non-zero. This is the case for polynomial rings. The field of fractions over an integral domain is the collection of (natural equivalence classes) of pairs consisting of a numerator and denominator. You can see above that a ratio of two polynomials lies in the field of fractions of the polynomial ring. We can also explicitly construct the field of fractions:
F = R.fraction_field()
(~p).parent() == F
True
F.is_field()
True
Cancelation is automatically done in a fraction field:
(x+1)/(x^2+2*x+1)
1/(x + 1)
(x+1)/((x+1)*(x+2))
1/(x + 2)
p
3*x^2 - 47
The roots
method for a polynomial gives you all the roots defined over the base ring or field (this is the field where the coefficients of the polynomial live). In this case the base ring is $\mathbb Q$, and our polynomial has no roots in this field:
p.roots()
[]
Here we construct a polynomial that does have roots in $\mathbb Q$ and observe what the roots
method produces:
q = (x-1)^2*(x-3/2)
print(f'q = {q}')
q.roots()
q = x^3 - 7/2*x^2 + 4*x - 3/2
[(3/2, 1), (1, 2)]
Observe that a list of pairs was returned. The pairs consist of the root and the multiplicity of the root. So, above we see that $1$ was a root with multiplicity $2$.
You can pass a different field to look for roots in a different (possibly larger field). Here we look for roots in AA
, the field of all algebraic real numbers.
p.roots(AA)
[(-3.958114029012639?, 1), (3.958114029012639?, 1)]
Fields of algebraic numbers¶
The algebraic closure of a $F$ field is the smallest field $\bar F$ containing $F$ such that every polynomial in the polynomial ring $F[x]$ has a root in $\bar F$. Equivalently if every polynomial in $F[x]$ can be factored into a product of degree one polynomials with coefficients in $\bar F$.
A special example is the algebraic closure of $\mathbb Q$. The complex numbers $\mathbb C$ are algebraically closed, but not all complex numbers are algebraic (the root of a polynomial with coefficients in $\mathbb Q$). It follows that there is the algebraic closure is intermediate between $\mathbb Q$ and $\mathbb C$. $$\mathbb Q \subset \bar {\mathbb Q} \subset \mathbb C.$$
(Technical remark: QQbar
is a bit more than the algebraic closure of $\mathbb Q$, it is the algebraic closure together with an inclusion in $\mathbb C$.)
The algebraic closure $\bar {\mathbb Q} \subset \mathbb C$ is denoted as follows:
QQbar
Algebraic Field
We can construct some elements:
sqrt(2).parent()
Symbolic Ring
We can convert sqrt(2)
into QQbar using the following:
QQbar(sqrt(2))
1.414213562373095?
Another root:
QQbar(5^(1/7))
1.258498950641827?
Sine and cosine of rational multiples of pi are always algebraic (because of the angle addition formulas).
x = QQbar(cos(5*pi/17))
x
0.602634636379257? + 0.?e-18*I
Algebraic numbers are roots of polynomials. The minimal polynomial is one that can not be factored over $\mathbb Q$, and is monic: the leading coefficient is one. We can get the minimal polynomial of an algebraic number with the minpoly()
method:
x.minpoly()
x^8 - 1/2*x^7 - 7/4*x^6 + 3/4*x^5 + 15/16*x^4 - 5/16*x^3 - 5/32*x^2 + 1/32*x + 1/256
We can construct all the third roots of $7$ in two ways. First by hand:
r1 = QQbar(7^(1/3))
r1
1.912931182772389?
r2 = r1 * QQbar(cos(2*pi/3) + I*sin(2*pi/3))
r2
-0.9564655913861945? + 1.656646999972302?*I
r2^3
7.000000000000000? + 0.?e-17*I
r2^3 == 7
True
r3 = r1 * QQbar(cos(4*pi/3) + I*sin(4*pi/3))
r3
-0.9564655913861945? - 1.656646999972302?*I
r3^3
7.000000000000000? + 0.?e-17*I
print(f'The third roots of 7 are {r1}, {r2}, and {r3}.')
The third roots of 7 are 1.912931182772389?, -0.9564655913861945? + 1.656646999972302?*I, and -0.9564655913861945? - 1.656646999972302?*I.
We could also construct the minimal polynomial and compute the roots within $\bar {\mathbb Q}$:
F.<x> = QQ[]
p = x^3 - 7
p
x^3 - 7
list_of_roots = p.roots(QQbar)
list_of_roots
[(1.912931182772389?, 1), (-0.9564655913861945? - 1.656646999972302?*I, 1), (-0.9564655913861945? + 1.656646999972302?*I, 1)]
Above we get a list of pairs consisting of the roots and their multiplicities. To access pair $i$, we use list_of_roots[i]
and to access the root we'd do list_of_roots[i][0]
. Here $i=0, 1, 2$. (To get the multiplicity of root $i$, we'd do list_of_roots[i][1]
.)
r1 = list_of_roots[0][0]
r2 = list_of_roots[1][0]
r3 = list_of_roots[1][0]
print(f'The third roots of 7 are {r1}, {r2}, and {r3}.')
The third roots of 7 are 1.912931182772389?, -0.9564655913861945? - 1.656646999972302?*I, and -0.9564655913861945? - 1.656646999972302?*I.
Algebraic Real Field¶
Inside $\mathbb Q$, we have the field of all algebraic real numbers: $\mathbb A = \bar {\mathbb Q} \cap \mathbb R:$
AA
Algebraic Real Field
Many of the numbers we constructed above were real and can be constructed in the same way. For example:
x = AA(cos(5*pi/17))
x
0.602634636379257?
Number fields¶
An algebraic number field is a finite degree field extension of $\mathbb Q$. These fields have the form $$\mathbb Q(r) = \{a_{d-1} r^{d-1} + a_{d-2} r^{d-2} + \ldots + a_0:~a_0, \ldots, a_{d-1} \in \mathbb Q\}.$$ where $r$ is a root of a (minimal) polynomial of degree $d$ with rational coefficients that does not split (have a non-trivial factorization into a product of polynomials with rational coefficients).
It is a standard result in algebra that $\mathbb Q(r)$ is a field. We discussed a bit more about this in class. If you want to learn more you could start with the wikipedia article or any standard algebra textbook covering field theory.
Below we define $F = \mathbb Q(r)$ where $r$ is a root of $x^2-3$.
R.<x> = QQ[]
R
Univariate Polynomial Ring in x over Rational Field
p = x^2 - 3
p
x^2 - 3
F.<sqrt3> = NumberField(p)
The above defines both F
and sqrt3
.
F
Number Field in sqrt3 with defining polynomial x^2 - 3
sqrt3
sqrt3
sqrt3.parent()
Number Field in sqrt3 with defining polynomial x^2 - 3
sqrt3^2
3
There are two embeddings of $F$ into $\mathbb A$. The one that sends sqrt3
to $\sqrt{3}$ and the one sending it to $-\sqrt{3}$:
F.embeddings(AA)
[ Ring morphism: From: Number Field in sqrt3 with defining polynomial x^2 - 3 To: Algebraic Real Field Defn: sqrt3 |--> -1.732050807568878?, Ring morphism: From: Number Field in sqrt3 with defining polynomial x^2 - 3 To: Algebraic Real Field Defn: sqrt3 |--> 1.732050807568878? ]
emb = F.embeddings(AA)[0]
emb(sqrt3)
-1.732050807568878?
emb(5+3*sqrt3)
-0.1961524227066319?
But because there are two, it doesn't make sense to convert numbers in the NumberField F
into AA
or other fields.
AA(sqrt3)
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[153], line 1 ----> 1 AA(sqrt3) File ~/Git/sagemath/sage/src/sage/structure/parent.pyx:908, in sage.structure.parent.Parent.__call__() 906 if mor is not None: 907 if no_extra_args: --> 908 return mor._call_(x) 909 else: 910 return mor._call_with_args(x, args, kwds) File ~/Git/sagemath/sage/src/sage/structure/coerce_maps.pyx:164, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_() 162 print(type(C), C) 163 print(type(C._element_constructor), C._element_constructor) --> 164 raise 165 166 cpdef Element _call_with_args(self, x, args=(), kwds={}): File ~/Git/sagemath/sage/src/sage/structure/coerce_maps.pyx:159, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_() 157 cdef Parent C = self._codomain 158 try: --> 159 return C._element_constructor(x) 160 except Exception: 161 if print_warnings: File ~/Git/sagemath/sage/src/sage/rings/qqbar.py:1154, in AlgebraicRealField._element_constructor_(self, x) 1152 raise ValueError("Cannot coerce algebraic number with non-zero imaginary part to algebraic real") 1153 elif hasattr(x, '_algebraic_'): -> 1154 return x._algebraic_(AA) 1155 return AlgebraicReal(x) File ~/Git/sagemath/sage/src/sage/rings/number_field/number_field_element.pyx:2875, in sage.rings.number_field.number_field_element.NumberFieldElement._algebraic_() 2873 emb = self._parent.coerce_embedding() 2874 if emb is None: -> 2875 raise ValueError("need a real or complex embedding to convert " 2876 "a non rational element of a number field " 2877 "into an algebraic number") ValueError: need a real or complex embedding to convert a non rational element of a number field into an algebraic number
But we can convert using an embedding as above.
You can also select an embedding when you construct the field. I like to first construct an element of QQbar
or AA
and then build the number field like this:
R.<x> = QQ[x]
p = x^3 - 7
p
x^3 - 7
roots = p.roots(QQbar)
roots
[(1.912931182772389?, 1), (-0.9564655913861945? - 1.656646999972302?*I, 1), (-0.9564655913861945? + 1.656646999972302?*I, 1)]
root_AA = roots[0][0]
root_AA
1.912931182772389?
root_AA.minpoly()
x^3 - 7
AA(7^(1/3)).minpoly()
x^3 - 7
F.<cubert7> = NumberField(root_AA.minpoly(), embedding=root_AA)
cubert7^3
7
AA(cubert7^2)
3.659305710022972?
Efficiency considerations¶
When possible, working in a number field is more efficient than working in $\mathbb A$. (At least for many tasks!) Here we demonstrate the speed difference:
import time
Construct an algebraic number $\sqrt[5]{7}$
r_AA = AA(sin(6*pi/11)^(1/5))
r_AA
0.9979559491516728?
We construct the number field generated by r_AA
. This number field will be F
and r
will be the generator (equal to r_AA
as a number).
F.<r> = NumberField(r_AA.minpoly(), embedding=r_AA)
r_AA == r
True
Below we compute a good rational approximation for this number using first AA
and then the number field F
constructed above. We see how many second passed before the calculation terminates. These rational approximations are constructed using continued fractions.
You can learn about continued fractions in this wikipedia article. I think we'll find time to discuss them later in the course.
import time
start = time.time()
cf = continued_fraction(r_AA)
ans1 = cf.convergent(100)
end = time.time()
print(f'Calculation took {end-start} seconds')
Calculation took 27.320870876312256 seconds
The number computed should be a rational:
ans1
1680754318589002894344686855877881366877587110/1684196902696710234922022092120958244523143943
ans1.parent() == QQ
True
It should also be very close to r_AA:
(r_AA-ans1).n()
7.64842151402072e-30
It is within $10^{-29}$. That is a pretty good approximation!
start = time.time()
cf = continued_fraction(r)
ans2 = cf.convergent(100)
end = time.time()
print(f'Calculation took {end-start} seconds')
Calculation took 5.230928421020508 seconds
The calculation was about 5 times faster for me in the NumberField. We briefly check that we got the same answer both times:
ans1 == ans2
True